#!/usr/bin/perl
# CCN::IPC::MessageStore
# Copyright (C) 2006, 2007  Stephane Alnet
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 

#
# For more information visit http://carrierclass.net/
#

=pod

    CCN::IPC::Message provides a timed and prioritized queue mechanism for
    action to be spooled either locally or remotely.

    Local events placed in the queue are removed by CCN::IPC::Unspool;
    remote events placed in the queue are removed bu CCN::IPC::UnspoolRemote.
    
=cut

use strict; use warnings;

package CCN::IPC::MessageStore;
use base qw(CCN::IPC::base);

=pod
    
    CCN::IPC::MessageStore is an abstract class. You cannot instantiate
    CCN::IPC::MessageStore, only a subclass that actually implements create_db().

    my $message_store = new CCN*::IPC::MessageStore;

    $message_store->insert($source_pk,$target_pk,$action,$priority)
        or
    $message_store->insert($source_pk,$target_pk,$action,$priority,$due_time)

=cut

sub insert
{
    my $self = shift;
    my ($source,$target,$content,$priority,$due) = @_;

    my $now = time();
    $due = $now if not defined $due;

    $self->cluck ('CCN::IPC::Message::insert: undefined source'  ) unless defined $source;
    $self->cluck ('CCN::IPC::Message::insert: undefined target'  ) unless defined $target;
    $self->cluck ('CCN::IPC::Message::insert: undefined content' ) unless defined $content;
    $self->cluck ('CCN::IPC::Message::insert: undefined priority') unless defined $priority;
    $self->cluck ('CCN::IPC::Message::insert: undefined due time') unless defined $due;

    my $db = $self->get_db();

    $db->sql_do('INSERT INTO messages (source,target,content,priority,due,created) VALUES (?,?,?,?,?,?)',
                $source,$target,$content,$priority,$due,$now)
        or return; # e.g. duplicate as created by SpoolRemote

    my $new_pk = $db->last_insert_id();
    return $new_pk;
}

=pod

    CCN::IPC::Message::get_next($target_pk)
    
        Returns the next message to be processed for the given target as a hashref.
        Returns undef if no messages are currently to be processed.

=cut

sub get_next($)
{
    my $self = shift;
    my ($target) = @_;
    $self->cluck('CCN::IPC::Message::get_next: undefined target') unless defined $target;

    my $now = time();

    my $db = $self->get_db();
    
    my $sth = $db->sql_do('SELECT * FROM messages WHERE target = ? AND due <= ? ORDER BY due ASC, priority ASC LIMIT 1',$target,$now);
    my $result = $sth->fetchrow_hashref;
    $sth->finish();

    return $result;
}

=pod

    CCN::IPC::Message::get_next_remote($target_pk)
    
        Returns the next message to be spooled to a remote target from $target_pk.

=cut

sub get_next_remote($)
{
    my $self = shift;
    my ($target) = @_;
    $self->cluck('CCN:IPC::Message::get_next_remote: undefined target') unless defined $target;

    my $db = $self->get_db();

    # Put rescheduled messages at the end of the queue
    my $sth = $db->sql_do('SELECT * FROM messages WHERE target <> ? ORDER BY rescheduled ASC, due ASC, priority ASC LIMIT 1',$target);
    my $result = $sth->fetchrow_hashref;
    $sth->finish();

    return $result;
}

=pod

    CCN::IPC::Message::clear($message_pk)

=cut

sub clear($)
{
    my $self = shift;
    my ($pk) = @_;
    $self->cluck('CCN::IPC::Message::clear: undefined pk') unless defined $pk;

    my $db = $self->get_db();
    
    # $db->sql_begin();
    $db->sql_do(<<'SQL',$pk);
        DELETE FROM messages WHERE pk = ?
SQL
    # $db->sql_commit();
}

=pod

    CCN::IPC::Message::reschedule($message_pk)

=cut

sub reschedule($)
{
    my $self = shift;
    my ($pk) = @_;

    $self->cluck ('CCN::IPC::Message::reschedule: undefined pk') unless defined $pk;

    my $db = $self->get_db();

    $db->sql_do('UPDATE messages SET rescheduled = rescheduled + 1 WHERE pk = ?',$pk)
        or $db->dbh_die('CCN::IPC::Message::reschedule');
}

1;
